#pragma once

#include "muduo/net/TcpServer.h"
#include "muduo/net/TcpConnection.h"
#include "muduo/net/EventLoop.h"

#include "proto/dispatcher.h"
#include "proto/codec.h"

#include "../proto_dir/log_proto.pb.h"

#include "connection.hpp"
/*

其实我觉得还是要看具体业务，如果具体业务有那种资源隔离（比如多租户等等）的需求，将连接细分为信道才有价值

对于那些业务模块本身就支持多线程安全访问，且没有资源隔离需求时，将连接细分为信道则纯属无用功，毫无意义

*/
namespace ns_log
{
    using namespace ns_log_proto;

    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    using openConnectionRequestPtr = std::shared_ptr<openConnectionRequest>;

    class LogServer
    {
    public:
        using ptr = std::shared_ptr<LogServer>;
        LogServer(uint16_t port)
            : _server(&_loop, muduo::net::InetAddress("0.0.0.0", port), "LogServer", muduo::net::TcpServer::Option::kReusePort), _dispatcher(std::bind(&LogServer::OnUnknownCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))), _conn_manager(std::make_shared<ConnectionManager>())
        {
            _dispatcher.registerMessageCallback<declareLoggerRequest>(std::bind(&LogServer::OnDeclareLoggerCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<sinklogRequest>(std::bind(&LogServer::OnSinkLogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<openConnectionRequest>(std::bind(&LogServer::OnOpenConnectionCallback,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            
            _server.setConnectionCallback(std::bind(&LogServer::OnConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        }

        void start()
        {
            // 设置监听套接字等等
            _server.start();
            // 设置事件循环
            _loop.loop();
        }

    private: // 事件注册
        void OnUnknownCallback(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp)
        {
            default_fatal("未知请求, 我们将断开连接");
            if (conn->connected())
                conn->shutdown();
        }

        void OnDeclareLoggerCallback(const muduo::net::TcpConnectionPtr &conn, const declareLoggerRequestPtr &req, muduo::Timestamp)
        {
            Connection::ptr my_conn = _conn_manager->getConnection(conn);
            if (my_conn.get() == nullptr)
            {
                basicCommonResponse(conn, req->rid(), false, "声明日志器失败,因为业务连接尚未建立");
                return;
            }
            my_conn->declareLogger(req);
        }

        void OnSinkLogCallback(const muduo::net::TcpConnectionPtr &conn, const sinklogRequestPtr &req, muduo::Timestamp)
        {
            Connection::ptr my_conn = _conn_manager->getConnection(conn);
            if (my_conn.get() == nullptr)
            {
                basicCommonResponse(conn, req->rid(), false, "打印日志失败,因为业务连接尚未建立");
                return;
            }
            my_conn->sinklog(req);
        }

        void OnOpenConnectionCallback(const muduo::net::TcpConnectionPtr &conn, const openConnectionRequestPtr &req, muduo::Timestamp)
        {
            _conn_manager->OpenConnection(req->connection_id(), conn, _codec);
            // 构建resp并发出
            basicCommonResponse(conn, req->rid(), true, "一切顺利");
        }

        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                default_info("连接建立成功");
            }
            else
            {
                // 删除该连接
                _conn_manager->CloseConnection(conn);
                conn->shutdown();
                default_info("连接断开成功");
            }
        }

        void basicCommonResponse(const muduo::net::TcpConnectionPtr &conn, const std::string &rid, bool ok, const std::string &err_msg)
        {
            // 构建resp并发出
            commonResponse resp;
            resp.set_rid(rid);
            resp.set_ok(ok);
            resp.set_err_msg(err_msg);
            _codec->send(conn, resp);
        }

    private:
        // 必须先构造loop，在构造server
        muduo::net::EventLoop _loop;
        muduo::net::TcpServer _server;

        // 必须先构造dispatcher，在构造codec
        ProtobufDispatcher _dispatcher;
        ProtobufCodecPtr _codec;

        ConnectionManager::ptr _conn_manager;
    };
}
